package util.accessArticle;


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 刷访csdn问量工具类
 */
public class AccessUtils {
    private static final Logger logger = LogManager.getLogger(AccessUtils.class);
    //    发送http请求的工具类
    private CopyOnWriteArraySet<String> myAllCategoryURL = new CopyOnWriteArraySet<>();//去重
    private CopyOnWriteArrayList<String> myAllArticleURL = new CopyOnWriteArrayList<>();//去重，并发访问
    static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
    static AtomicInteger counter = new AtomicInteger();
    static ExecutorService executorService = Executors.newFixedThreadPool(2);
    private AtomicLong count = new AtomicLong(0);
    //    static Logger logger = Logger.getLogger(AccessUtils.class);
    static List<String> fakeIps = Arrays.asList(
            "82.97.215.243",
            "113.223.212.176",
            "74.208.177.198",
            "190.111.214.234",
            "124.106.150.231",
            "42.63.65.78",
            "41.203.83.66",
            "45.116.83.74",
            "148.251.88.109 ",
            "190.187.201.26 ",
            "161.97.144.14",
            "103.181.168.193",
            "45.177.98.132",
            "201.184.127.109",
            "109.254.30.74",
            "8.130.36.245");

    /**
     * 获取博主所有的分类专栏访问地址
     * 例如：https://blog.csdn.net/weixin_51389615?type=blog
     *
     * @param url
     */
    public void addAllBlogCategoryURL_By_URL(String url) {
        myAllCategoryURL.clear();//清空之前存的所有分类url集合
        ResponseEntity<String> htmlString = new RestTemplate().getForEntity(url, String.class);
        String html = htmlString.toString();//将获取的网页转换成字符串
        //获取html元素
        Document doc = Jsoup.parse(html);
        // 获取id=asideCategory的标签,这个标签下存放的是包含分类专栏的标签，缩小范围
        Element asideCategory = doc.getElementById("asideCategory");
        //  获取id=asideCategory的标签下的ul标签
        Elements ultag = asideCategory.getElementsByTag("ul");
        // 获取id=asideCategory的标签下的ul标签的a标签===>这个标签的href存放的就是分类专栏地址
        Elements a_s = ultag.get(0).getElementsByTag("a");
        //遍历a标签，获取a标签中的href属性值
        a_s.parallelStream().forEach((a) -> {
            String href = a.attr("href");
            if (!href.isEmpty()) {
                myAllCategoryURL.add(href);//并行流添加到集合中
            }
        });

        System.out.println("总共有" + myAllCategoryURL.size() + "个专栏");
//        logger.info("总共有" + myAllCategoryURL.size() + "个专栏");
    }

    /**
     * 根据分类专栏的地址获取分类专栏下的所有文章
     *
     * @param url
     * @return
     */
    public boolean addAllArticle_By_CategoryURL(String url) {
        ResponseEntity<String> htmlString = new RestTemplate().getForEntity(url, String.class);
        String html = htmlString.toString();//将获取的网页转换成字符串
        // 获取html元素
        Document doc = Jsoup.parse(html);
        String title = doc.getElementById("column").getElementsByClass("column_title oneline").get(0).text();//获取专栏标题

        //1、 获取类名为column_article_list的html标签，这个标签内存放的就是文章的列表
        Elements column_article_list = doc.getElementsByAttributeValue("class", "column_article_list");
        //2、 获取专栏中所有文章的li元素
        Elements li_s = column_article_list.get(0).getElementsByTag("li");
        //3、并行流处理，遍历获取文章url
        li_s.parallelStream().forEach((li) -> {
            String href = li.getElementsByTag("a").attr("href");
            if (!href.isEmpty()) {
                myAllArticleURL.add(href);//存入成员变量中
            }
        });

//        System.out.println(title + "专栏有" + li_s.size() + "篇文章");
//        logger.info(title + "专栏有" + li_s.size() + "篇文章");
        return true;
    }

    public CopyOnWriteArrayList<String> getAllArticleUrl(String url) {

        ResponseEntity<String> htmlString = new RestTemplate().getForEntity(url, String.class);
        String html = htmlString.toString();//将获取的网页转换成字符串
        // 获取html元素
        Document doc = Jsoup.parse(html);
        Elements elementsByTag = doc.getElementsByClass("mainContent").get(0).getElementsByTag("a");//获取专栏标题

        for (Element element : elementsByTag) {
            String href = element.attr("href");
            if (!href.isEmpty()) {
                myAllArticleURL.add(href);//存入成员变量中
            }
        }

        return myAllArticleURL;//所有文章地址
    }

    /**
     * 定时的访问文章详情，模拟用户点击文章
     *
     * @param url
     * @param scheduleTime
     */
    public void accessCSDNArticle(String url, Long scheduleTime) {
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            String fakeIp = getRandomIp();

            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            // 设置X-Forwarded-For请求头
            headers.set("X-Forwarded-For", fakeIp);
            headers.set("Proxy-Client-IP", fakeIp);
            headers.set("WL-Proxy-Client-IP", fakeIp);
            HttpEntity<?> entity = new HttpEntity<>(headers);

            ResponseEntity<String> forEntity = restTemplate.exchange(
                    url,
                    HttpMethod.GET,
                    entity,
                    String.class
            );

            if (forEntity.getStatusCode() == HttpStatus.OK) {
                logger.info("第{}次访问成功, 访问链接: {}", counter.addAndGet(1), url);
            } else {
                logger.error("第{}次访问成功, 访问链接: {}", counter.addAndGet(1), url);
            }
        }, 0, scheduleTime, TimeUnit.SECONDS);
    }

    synchronized String getRandomIp() {
        Random random = new Random();
        int randomIndex = random.nextInt(fakeIps.size());
        return fakeIps.get(randomIndex);
    }

    /**
     * 重新来一遍
     *
     * @param url
     */
    public void updateUrl(String url) {
        this.myAllCategoryURL.clear();//清空所有专栏地址
        this.myAllArticleURL.clear();//清空之前的文章
        getAllArticleUrl(url);//重新获取所有文章链接到myAllArticleURL中
    }


    /**
     * 前面一次性获取来所有博客链接，并保存了下来。这样就不需要再次发送获取所有文章的请求
     *
     * @param url          传入博主自己的url主页，例如：https://blog.csdn.net/weixin_51389615?type=blog
     * @param scheduleTime 休眠时间，单位秒
     */
    public void autoRefresh(String url, long scheduleTime) {
        getAllArticleUrl(url);//获取所有文章链接，文章链接会存在成员变量myAllArticleURL中

        this.myAllArticleURL.stream().forEach(articleUrl -> {
            accessCSDNArticle(articleUrl, scheduleTime);//定时的访问，防止刷新太快被封ip
        });
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            updateUrl(url);//每2小时更新一次url
        }, 0, 2, TimeUnit.HOURS);
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            System.gc();
        }, 0, 65, TimeUnit.SECONDS);
    }


    /**
     * @Description 按照顺序访问指定文章，(第一条，或者第n条)
     * @Author Yang.Mou
     * @Date 19/03/2025
     */
    public void autoRefreshDIY(String url, long scheduleTime, List<Integer> articleNos) {
        getAllArticleUrl(url);//获取所有文章链接，文章链接会存在成员变量myAllArticleURL中

        for (int i = 0; i < myAllArticleURL.size(); i++) {
            if (articleNos.contains(i + 1)) {
                accessCSDNArticle(myAllArticleURL.get(i), scheduleTime);//定时的访问，防止刷新太快被封ip
            }

        }
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            updateUrl(url);//每2小时更新一次url
        }, 0, 2, TimeUnit.HOURS);
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            System.gc();
        }, 0, 65, TimeUnit.SECONDS);
    }

    /**
     * @Description 指定单个url
     * @Author Yang.Mou
     * @Date 20/03/2025
     */
    public void specifySingleUrl(String url, long scheduleTime) {
        accessCSDNArticle(url, scheduleTime);
    }
}
